﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WMemoryProfiler.Memory
{
    /// <summary>
    /// Define a diff threshold used to assert when a specific condition is met
    /// </summary>
    public class DiffThreshold
    {
        /// <summary>
        /// Optional name for threshold instance
        /// </summary>
        public string Name { get; set;  }

        /// <summary>
        /// Substring of type name, or null when all types are used. Matching is case insensitive.
        /// </summary>
        public string Type { get; set;  }

        /// <summary>
        /// How many objects can be added before it will assert.
        /// if AddedObjectCount > MaxAddedInstances then assert
        /// </summary>
        public long MaxAddedInstances { get; set; }

        /// <summary>
        /// How much additional memory can be allocated before it an assert is raised.
        /// if AddedBytes > MaxAddedBytes then assert
        /// </summary>
        public long MaxAddedBytes { get; set;  }

        /// <summary>
        /// Maximum number of objects of given type can exist before an assert is done.
        /// if Count > MaxTotalInstanceCount then assert
        /// When you can to assert that no instance does exist anymore
        /// you can set MaxTotalInstance = 0
        /// </summary>
        public long MaxTotalInstances { get; set;  }

        /// <summary>
        /// Maximum bytes all object of given type can consume on the managed heap
        /// if AllBytes > MaxTotalBytes then assert
        /// </summary>
        public long MaxTotalBytes { get; set; }
        
        /// <summary>
        /// Init with default values. 
        /// </summary>
        public DiffThreshold()
        {
            MaxAddedInstances = long.MaxValue;
            MaxAddedBytes = long.MaxValue;
            MaxTotalInstances = long.MaxValue;
            MaxTotalBytes = long.MaxValue;
        }

        internal struct AssertItem
        {
            public string AssertedProperty;
            public long MaxValue;
            public long CurrentValue;
            public AssertItem(long currentValue, string assertedProperty, long maxValue)
            {
                AssertedProperty = assertedProperty;
                MaxValue = maxValue;
                CurrentValue = currentValue;
            }
        }

        internal void Assert(InstanceInfoDiff[] diffs, DiffAssert asserter)
        {
            long addedInstances = 0;
            long totalInstances = 0;
            long addedBytes = 0;
            long totalBytes = 0;

            foreach (var diff in diffs)
            {
                // type filter
                if (!String.IsNullOrEmpty(Type))
                {
                    if (diff.TypeName.IndexOf(Type, StringComparison.OrdinalIgnoreCase) == -1)
                    {
                        continue;
                    }
                }

                totalInstances += (long) diff.InstanceCount;
                addedBytes += (long) diff.AllocatedBytesDiff;
                totalBytes += (long) diff.AllocatedBytes;
                addedInstances += (long) diff.InstanceCountDiff;
            }

            List<AssertItem> violations = new List<AssertItem>();
            if (addedInstances > MaxAddedInstances)
            {
                violations.Add(new AssertItem(addedInstances, "MaxAddedInstances", this.MaxAddedInstances));
            }
            if( addedBytes > MaxAddedBytes)
            {
                violations.Add(new AssertItem(addedBytes, "MaxAddedBytes", this.MaxAddedBytes));
            }
            if( totalBytes > MaxTotalBytes )
            {
                violations.Add(new AssertItem(totalBytes, "MaxTotalBytes", this.MaxTotalBytes));
            }
            if( totalInstances > MaxTotalInstances)
            {
                violations.Add(new AssertItem(totalInstances, "MaxTotalInstances", this.MaxTotalInstances));
            }

            if( violations.Count > 0 )
            {
                asserter.Assert(this, violations);
            }
        }

 
    }
}
